package com.han.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doudian.open.api.product_listV2.ProductListV2Request;
import com.doudian.open.api.product_listV2.ProductListV2Response;
import com.doudian.open.api.product_listV2.data.DataItem;
import com.doudian.open.api.product_listV2.param.ProductListV2Param;
import com.doudian.open.api.token.AccessTokenData;
import com.doudian.open.core.AccessToken;
import com.doudian.open.core.AccessTokenBuilder;
import com.han.core.domain.bo.ProductPageBO;
import com.han.core.domain.dto.QueryProductPageDTO;
import com.han.core.domain.dto.UpdateProductStatusDTO;
import com.han.core.domain.entity.Product;
import com.han.core.domain.entity.ProductCategory;
import com.han.core.domain.vo.ProductPageVO;
import com.han.mapper.ProductMapper;
import com.han.service.ProductCategoryService;
import com.han.service.ProductService;
import com.han.service.ProductSkuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.han.Application.APP_KEY;
import static com.han.Application.APP_SECRET;
import static com.han.service.impl.ProductSkuServiceImpl.SplitList;

/**
 * @author Administrator
 */
@Service
@Slf4j
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Qualifier("threadPoolTaskExecutor")
    @Resource
    ThreadPoolTaskExecutor executor;

    @Resource
    @Lazy
    ProductCategoryService productCategoryService;
    @Resource
    @Lazy
    ProductSkuService productSkuService;


    @Async("threadPoolTaskExecutor")
    @Override
    public void pullAllProductAsync(AccessTokenData accessTokenData) {
        log.info("异步拉取商品信息开始：[{}]", LocalDateTime.now());
        AccessToken accessToken = AccessTokenBuilder.parse(accessTokenData.getAccessToken());
        final long size = 400L;
//        Snowflake snowflake = IdUtil.getSnowflake();
        AtomicInteger max = new AtomicInteger(Integer.MAX_VALUE);
        Set<Long> productIdSet = baseMapper.selectProductIds(Long.valueOf(accessTokenData.getShopId()));
        //记录插入条数
        AtomicInteger count = new AtomicInteger(0);
        List<CompletableFuture<Map<Long, Long>>> futures = new ArrayList<>();
        // 修改1.先掉一次获取总条数，固定页码数量
        ProductListV2Request preReq = new ProductListV2Request();
        ProductListV2Param preParam = new ProductListV2Param();
        preParam.setPage(1L);
        preParam.setSize(1L);
        preReq.setParam(preParam);
        ProductListV2Response re = preReq.execute(accessToken);
        max.set((int) Math.ceil((double) re.getData().getTotal().intValue() / size));
        for (long i = 1; i <= max.get(); i++) {
            long finalPage = i;
            futures.add(CompletableFuture.supplyAsync(() -> {
                ProductListV2Request listRequest = new ProductListV2Request();
                ProductListV2Param param = new ProductListV2Param();
                param.setPage(finalPage);
                param.setSize(size);
                listRequest.setParam(param);
                ProductListV2Response response = listRequest.execute(accessToken);
                if (!response.getCode().equals("10000")) {
                    log.warn("请求获取商品列表出现异常,msg:{}", response.getMsg());
                    return null;
                }
                List<Product> productList = new ArrayList<>((int) size);
                List<ProductCategory> productCategoryList = new ArrayList<>((int) size);
                Map<Long, Long> skuMap = new HashMap<>((int) Math.ceil(size * 1.5));
                //循环获得的为单个对象
                for (DataItem x : response.getData().getData()) {
                    if (productIdSet.contains(x.getProductId())) {
                        //剔除数据库中已存在的数据
                        continue;
                    }
                    Product product = new Product();
//                    long productOwnId = snowflake.nextId();
//                    product.setProductOwnId(productOwnId);
                    product.setShopId(Long.valueOf(accessTokenData.getShopId()));
                    product.setCreateTime(new Date(x.getCreateTime()));
                    BeanUtils.copyProperties(x, product);
                    productList.add(product);
                    ProductCategory productCategory = new ProductCategory();
                    BeanUtils.copyProperties(x.getCategoryDetail(), productCategory);
//                    productCategory.setProductOwnId(productOwnId);
                    productCategory.setProductId(x.getProductId());
                    productCategory.setShopId(Long.valueOf(accessTokenData.getShopId()));
                    productCategoryList.add(productCategory);
                    skuMap.put(x.getProductId(), x.getOutProductId());
                }
                this.saveBatch(productList);
                productCategoryService.saveBatch(productCategoryList);
                count.getAndAdd(productList.size());
                return skuMap;
            }, executor).thenApply((x) -> {
                productSkuService.saveProductSkuByProductId(accessTokenData, x);
                return x;
            }));
        }
        CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]);
        CompletableFuture.allOf(futuresArray).join();
        log.info("异步拉取商品信息结束：[{}];拉取商品总条数：[{}],插入商品总条数:[{}]", LocalDateTime.now(), re.getData().getTotal(), count.get());
    }

    @Override
    public void pullProductByIds(AccessTokenData accessTokenData, List<Long> ids) {
        //先拉去当前商品， sdk要求一次最多只能传100个；
        List<List<Long>> lists = new ArrayList<>();
        if (ids.size() > 100) {
            lists = SplitList(ids, 100);
        } else {
            lists.add(ids);
        }
        Set<Long> allIdsSet = this.baseMapper.selectProductIds(Long.valueOf(accessTokenData.getShopId()));
        for (List<Long> list : lists) {
            List<String> collect = list.stream().map(Object::toString).collect(Collectors.toList());
            //存储100或小于100的ids
            ProductListV2Request request = new ProductListV2Request();
            ProductListV2Param param = new ProductListV2Param();
            param.setProductId(collect);
            param.setPage(1L);
            param.setSize(100L);
            request.setParam(param);
            ProductListV2Response response = request.execute(AccessTokenBuilder.parse(accessTokenData.getAccessToken()));
            if (!response.getCode().equals("10000")) {
                log.warn("请求获取商品列表出现异常,msg:{}", response.getMsg());
                return;
            }
            List<DataItem> data = response.getData().getData();
            List<Product> productList = new ArrayList<>();
            List<ProductCategory> productCategoryList = new ArrayList<>();
            data.forEach(x -> {
                if (allIdsSet.contains(x.getProductId())) {
                    //剔除以存在的商品；
                    return;
                }
                Product product = new Product();
                BeanUtils.copyProperties(x, product);
                product.setShopId(Long.valueOf(accessTokenData.getShopId()));
                product.setCreateTime(new Date(x.getCreateTime()));
                productList.add(product);
                ProductCategory productCategory = new ProductCategory();
                BeanUtils.copyProperties(x.getCategoryDetail(), productCategory);
                productCategory.setProductId(x.getProductId());
                productCategoryList.add(productCategory);
            });
            this.saveBatch(productList);
            productCategoryService.saveBatch(productCategoryList);
        }
        Map<Long, Long> map = new HashMap<>();
        ids.forEach(x -> map.put(x, 0L));
        productSkuService.saveProductSkuByProductId(accessTokenData, map);
    }

    @Override
    public Page<ProductPageVO> PageByQuery(QueryProductPageDTO queryProductDTO) {
        //总条数就是先查一条count(1) 统计一下，然后在进行分页查询;如果接口查询过慢的话可以走异步
        Integer total = baseMapper.getPageTotal(queryProductDTO);
        List<ProductPageVO> resultList = new ArrayList<>();
        queryProductDTO.setStart(queryProductDTO.getPage() == 1 ? 0 : queryProductDTO.getPage() * queryProductDTO.getSize());
        queryProductDTO.setEnd(queryProductDTO.getPage() * queryProductDTO.getSize());
        List<ProductPageBO> pageBO = this.baseMapper.PageByQuery(queryProductDTO);
        Map<Long, List<ProductPageBO>> map = pageBO.stream().collect(Collectors.groupingBy(ProductPageBO::getProductId));
        for (Long key : map.keySet()) {
            List<ProductPageBO> productPageBOS = map.get(key);
            //price不可能为空，
            long minPrice = 0L;
            long maxPrice = 0L;
            for (ProductPageBO bo : productPageBOS) {
                if (bo.getPrice() == null) {
                    continue;
                }
                if (minPrice == 0) {
                    minPrice = bo.getPrice();
                }
                if (maxPrice == 0) {
                    maxPrice = bo.getPrice();
                }
                if (minPrice < bo.getPrice()) {
                    minPrice = bo.getPrice();
                }
                if (maxPrice > bo.getPrice()) {
                    maxPrice = bo.getPrice();
                }
            }
            BigDecimal minBigDecimal = BigDecimal.valueOf(minPrice).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
            BigDecimal maxDecimal = BigDecimal.valueOf(maxPrice).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
            ProductPageBO productPageBO = productPageBOS.get(0);
            ProductPageVO productPageVO = new ProductPageVO();
            productPageVO.setProductId(productPageBO.getProductId().toString());
            BeanUtils.copyProperties(productPageBO, productPageVO);
            //设置金额区间
            productPageVO.setPriceRange(minBigDecimal.toString().concat(" - ").concat(maxDecimal.toString()));
            //设置销售金额
            productPageVO.setDiscountPrice(BigDecimal.valueOf(productPageBO.getDiscountPrice()).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP));
            //设置类别拼接str
            StringBuilder sb = new StringBuilder();
            final String specialSeparator = ">";
            appendStr(sb, productPageBO.getFirstCname(), specialSeparator);
            appendStr(sb, productPageBO.getSecondCname(), specialSeparator);
            appendStr(sb, productPageBO.getThirdCname(), specialSeparator);
            appendStr(sb, productPageBO.getFourthCname(), specialSeparator);
            productPageVO.setConcatCategoryStr(sb.toString());
            //库存判断数据计算方法：sku_type = 0时使用stock_num,当sku_type = 1时使用 stock_map
            AtomicInteger stock = new AtomicInteger(0);
            productPageBOS.forEach(x -> {
                //可能存在有主商品没有对应的sku;
                if (ObjectUtil.isNull(x.getSkuType())) {
                    log.warn("商品:[{}],缺少对应的sku信息;", x.getProductId());
                    return;
                }
                if (x.getSkuType() == 0 && ObjectUtil.isNotNull(x.getStockNum())) {
                    stock.getAndAdd(x.getStockNum());
                } else if (x.getSkuType() == 1 && StringUtils.hasText(x.getStockMapJsonStr()) && !Objects.equals(x.getStockMapJsonStr(), "null")) {
                    stock.getAndAdd(strMatchesAndCount(x.getProductId(), x.getStockMapJsonStr()));
                }
            });
            productPageVO.setStock(stock.intValue());
            resultList.add(productPageVO);
        }
        Page<ProductPageVO> page = new Page<>();
        page.setRecords(resultList);
        page.setTotal(total);
        page.setSize(queryProductDTO.getSize());
        page.setCurrent(queryProductDTO.getPage());
        return page;
    }

    public static void appendStr(StringBuilder sb, String str, final String specialSeparator) {
        if (StringUtils.hasText(str)) {
            if (sb.length() == 0) {
                sb.append(str);
            } else {
                sb.append(specialSeparator).append(str);
            }
        }
    }

    public static Integer strMatchesAndCount(Long productId, String jsonStr) {
        int count = 0;
        String[] split = jsonStr.trim().replace("{", "").replace("}", "").split(",");
        for (String s : split) {
            String substring = s.substring(s.indexOf(":") + 1).trim();
            boolean matches = substring.matches("[0-9]*");
            if (matches) {
                count += Integer.parseInt(substring);
            } else {
                log.warn("给与json结构体数据剖析异常：[{}]，异常商品id:[{}]", jsonStr, productId);
                //不抛出异常中断了，因为有脏数据；
                //product_id:3688095098165395587,value {",!?.;';": 0}
//                throw new RuntimeException("json结构体剖析异常");
            }
        }
        return count;
    }


    @Override
    public Map<Integer, Map<Long, String>> updateProductStatus(AccessTokenData accessTokenData, UpdateProductStatusDTO dto) {
        //结果集
        Map<Integer, Map<Long, String>> countMap = new HashMap<>();
//        for (Long productId : dto.getProductIds()) {
        List<Long> canUpdateProductIds = new ArrayList<>();
        List<CompletableFuture<Void>> future = new ArrayList<>();
        dto.getProductIds().forEach(productId -> future.add(CompletableFuture.runAsync(() -> {
            boolean isChange = false;
            https:
//openapi-fxg.jinritemai.com/product/setOnline?access_token=9779afc7-1c45-478f-91ee-20e25ab129fe&app_key=7353558278103811611&method=product.setOnline&param_json=%7B%22product_id%22%3A3691417545295921234%7D&sign=6e3a9efc4b1311076cd205f8a9e9aab3&timestamp=2024-06-24+14%3A44%3A45&v=2
            if (dto.getStatus() == 0) {
                final String url = "https://openapi-fxg.jinritemai.com/product/setOnline";
                Map<String, Object> params = new HashMap<>();
                params.put("app_key", APP_KEY);
                params.put("method", "product.setOnline");
                params.put("access_token", accessTokenData.getAccessToken());
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("product_id", productId);
                params.put("param_json", jsonObject.toJSONString());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                String format = sdf.format(new Date());
                params.put("timestamp", format);
                params.put("v", "2");
                String sign1 = com.doudian.open.utils.SignUtil.sign(APP_KEY, APP_SECRET, "product.setOnline", format, jsonObject.toString(), "2");
                params.put("sign", sign1);
                String params1 = HttpUtil.toParams(params);
                String post = HttpUtil.post(url + "?" + params1, jsonObject);
                JSONObject response = JSONObject.parseObject(post);
                if (response.getInteger("code") != 10000) {
                    Map<Long, String> map = new HashMap<>();
                    map.put(productId, response.getString("sub_msg"));
                    countMap.put(0, map);
                    log.warn("请求上架商品：[{}]失败,失败返回:[{}]", productId, response);
                } else {
                    log.info("更新商品为上架:[{}],成功:[{}]", productId, response);
                    isChange = true;
                }
            }
            if (dto.getStatus() == 1) {
                final String url = "https://openapi-fxg.jinritemai.com/product/setOffline";
                Map<String, Object> params = new HashMap<>();
                params.put("app_key", APP_KEY);
                params.put("method", "product.setOffline");
                params.put("access_token", accessTokenData.getAccessToken());
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("product_id", productId);
                params.put("param_json", jsonObject.toJSONString());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                String format = sdf.format(new Date());
                params.put("timestamp", format);
                params.put("v", "2");
                String sign = com.doudian.open.utils.SignUtil.sign(APP_KEY, APP_SECRET, "product.setOffline", format, jsonObject.toString(), "2");
                params.put("sign", sign);
                String params1 = HttpUtil.toParams(params);
                String post = HttpUtil.post(url + "?" + params1, jsonObject);
                JSONObject response = JSONObject.parseObject(post);
                if (response.getInteger("code") != 10000) {
                    Map<Long, String> map = new HashMap<>();
                    map.put(productId, response.getString("sub_msg"));
                    countMap.put(0, map);
                    log.warn("请求下架商品：[{}]失败,失败返回:[{}]", productId, response);
                } else {
                    log.info("更新商品为下架:[{}],成功:[{}]", productId, response);
                    isChange = true;
                }
            }
            if (dto.getStatus() == 2) {
                final String url = "https://openapi-fxg.jinritemai.com/product/del";
                Map<String, Object> params = new HashMap<>();
                params.put("app_key", APP_KEY);
                params.put("method", "product.del");
                params.put("access_token", accessTokenData.getAccessToken());
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("product_id", productId);
                params.put("param_json", jsonObject.toJSONString());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                String format = sdf.format(new Date());
                params.put("timestamp", format);
                params.put("v", "2");
                String sign = com.doudian.open.utils.SignUtil.sign(APP_KEY, APP_SECRET, "product.del", format, jsonObject.toString(), "2");
                params.put("sign", sign);
                String params1 = HttpUtil.toParams(params);
                String post = HttpUtil.post(url + "?" + params1, jsonObject);
                JSONObject response = JSONObject.parseObject(post);
                if (response.getInteger("code") != 10000) {
                    Map<Long, String> map = new HashMap<>();
                    map.put(productId, response.getString("sub_msg"));
                    countMap.put(0, map);
                    log.warn("请求删除商品：[{}]失败,失败返回:[{}]", productId, response);
                } else {
                    log.info("更新商品为删除:[{}],成功:[{}]", productId, response);
                    isChange = true;
                }
            }
            //先进行远程更新，远程更新成功则更新数据库；
            if (isChange) {
                canUpdateProductIds.add(productId);
            }
        }, executor)));
        CompletableFuture[] futuresArray = future.toArray(new CompletableFuture[0]);
        CompletableFuture.allOf(futuresArray).join();
        UpdateWrapper<Product> wrapper = new UpdateWrapper<>();
        wrapper.lambda().set(Product::getStatus, dto.getStatus())
                .set(Product::getUpdateTime, new Date())
                .eq(Product::getShopId, Long.valueOf(accessTokenData.getShopId()))
                .in(Product::getProductId, canUpdateProductIds);
        this.update(wrapper);
        if (countMap.containsKey(0)) {
            log.warn("更新失败条数：[{}],列表值为：[{}]", countMap.get(0).size(), countMap.get(0));
        }
        return countMap;

    }


}




